package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/duke-git/lancet/v2/convertor"
	"github.com/duke-git/lancet/v2/formatter"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/duke-git/lancet/v2/xerror"
	"github.com/zeromicro/go-zero/core/mr"
	"io"
	"math"
	"net/http"
	"os"
	"strings"
	"time"
	"wsgr-recognize/tool"
	"wsgr-recognize/types"
)

var TemplateData = make(map[string][]int64)

var port = 8080
var pings, _ = time.ParseDuration("86400s")

func main() {
	tk := time.NewTicker(pings)
	defer tk.Stop()

	go func() {
		for {
			select {
			case <-tk.C:
				os.Exit(1)
			default:
				//time.Sleep(1 * time.Second)
				//fmt.Println(time.Now().Format(time.DateTime))
			}
		}
	}()

	initFile()
	initReqFile()

	http.Handle("/ping", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tk.Reset(pings)

		_, _ = w.Write([]byte("pong"))
	}))

	http.Handle("/enemy", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		all, err := io.ReadAll(r.Body)
		if err != nil {
			fmt.Println(err)
			return
		}
		req := &enemyReq{
			EnemyFleet: make([][]int64, 0),
		}
		err = json.Unmarshal(all, &req.EnemyFleet)
		if err != nil {
			fmt.Println(err)
			return
		}
		resp := syncImg(req)
		marshal, err := json.Marshal(resp)
		if err != nil {
			fmt.Println(err)
			return
		}
		_, _ = w.Write(marshal)
		fmt.Println(string(marshal))
		tk.Reset(pings)
	}))

	fmt.Printf("Server started %d \n", port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	if err != nil {
		fmt.Printf("err %v", err)

		panic(err)
	}

}

func initFile() {
	getwd, err := os.Getwd()
	if err != nil {
		return
	}
	fmt.Println(getwd)
	file, err := os.ReadFile("TemplateData")
	if err != nil {
		fmt.Printf("err %s dir:%s\n", err, getwd)
		panic(err)
	}
	mp := strings.Split(string(file), "\n")[1:]
	// 去除空字符串
	for i := 0; i < len(mp); i++ {
		if mp[i] == "" {
			mp = append(mp[:i], mp[i+1:]...)
			i--
		}
	}
	for len(mp) > 0 {
		key := mp[0]

		var value []int64
		for _, str := range mp[2:18] {
			for _, point := range strings.Split(str, " ") {
				toInt, err := convertor.ToInt(point)
				if err != nil {
					//fmt.Println(str)
					fmt.Printf("str: %s,err %v \n", point, err)
					return
				}
				value = append(value, toInt)
			}
		}

		TemplateData[key] = value
		mp = mp[18:]
	}
	input, err := formatter.Pretty(TemplateData)
	if err != nil {
		err = xerror.Wrap(err)
		fmt.Printf("err %+v", err)
		return
	}
	err = os.WriteFile("templateData.json", []byte(input), 0755)
	if err != nil {
		err = xerror.Wrap(err)
		fmt.Printf("err %+v", err)
		return
	}
}

func initReqFile() {
	var tempReqData [][]int64

	file, err := os.ReadFile("args.in")
	if err != nil {
		fmt.Printf("err %v \n", err)
		panic(err)
	}
	mp := strings.Split(string(file), "\n")[2:]
	// 去除空字符串
	for i := 0; i < len(mp); i++ {
		if mp[i] == "" {
			mp = append(mp[:i], mp[i+1:]...)
			i--
		}
	}
	for len(mp) > 0 {
		//key := mp[0]

		var value []int64
		for _, str := range mp[1:17] {
			for _, point := range strings.Split(str, " ") {
				if point == "" {
					continue
				}
				toInt, err := convertor.ToInt(point)
				if err != nil {
					fmt.Println(str)
					fmt.Printf("str: %s,err %v \n", point, err)
					return
				}
				value = append(value, toInt)
			}
		}
		tempReqData = append(tempReqData, value)
		mp = mp[17:]
	}

	input, err := formatter.Pretty(tempReqData)
	if err != nil {
		err = xerror.Wrap(err)
		fmt.Printf("err %+v", err)
		return
	}
	err = os.WriteFile("args.json", []byte(input), 0755)
	if err != nil {
		err = xerror.Wrap(err)
		fmt.Printf("err %+v", err)
		return
	}
}

type enemyReq struct {
	EnemyFleet [][]int64 `json:"enemy_fleet"`
}
type enemyResp struct {
	Result []string `json:"result"`
}

// 图片相似度：余弦相似度（Cosine Similarity）值越接近1，则相似度越高。
func syncImg(req *enemyReq) enemyResp {
	resp := enemyResp{
		Result: make([]string, 0),
	}

	var tempData []*types.EnemyRes
	err := mr.MapReduceVoid(func(source chan<- *types.Enemy) {
		for k, item := range req.EnemyFleet {
			source <- &types.Enemy{
				Sort: int64(k),
				Data: item,
			}
		}
	}, func(item *types.Enemy, writer mr.Writer[*types.EnemyRes], cancel func(error)) {
		// 一号位匹配所有可能的模板
		//res := similarityOne(&item.Data)
		var float64Slice []float64
		for _, value := range item.Data {
			float64Slice = append(float64Slice, float64(value))
		}
		res := tool.AbsOne(&float64Slice, TemplateData)
		//res := similarityOne(&item.Data)
		if len(res) <= 0 {
			err := errors.New("no template find")
			cancel(err)
		}
		EnemyRes := &types.EnemyRes{
			Sort: item.Sort,
			Type: res[0].Type,
		}

		writer.Write(EnemyRes)
	}, func(pipe <-chan *types.EnemyRes, cancel func(error)) {
		for item := range pipe {
			tempData = append(tempData, item)
		}
	})
	if err != nil {
		fmt.Println(err)
		return enemyResp{}
	}
	// 按照进来的顺序返回
	slice.SortBy(tempData, func(a, b *types.EnemyRes) bool {
		return a.Sort < b.Sort
	})
	for _, item := range tempData {
		resp.Result = append(resp.Result, item.Type)
	}

	return resp
}

type similarityRes struct {
	Key   string
	Value float64
}

func similarityOne(in *[]int64) []*similarityRes {
	var res []*similarityRes
	for key, v := range TemplateData {
		similarity := cosineSimilarity(in, &v)
		var similarityRes similarityRes
		similarityRes.Key = key
		similarityRes.Value = similarity
		res = append(res, &similarityRes)
	}

	return res
}

// 计算两个向量的点积
func dotProduct(v1, v2 *[]int64) float64 {
	var sum int64
	for i := range *v1 {
		vs1 := *v1
		vs2 := *v2
		sum += vs1[i] * vs2[i]
	}
	return float64(sum)
}

// 计算向量的模
func magnitude(v *[]int64) float64 {
	var sum int64
	for _, val := range *v {
		sum += val * val
	}
	return math.Sqrt(float64(sum))
}

// 计算两个向量的余弦相似度
func cosineSimilarity(v1, v2 *[]int64) float64 {
	return dotProduct(v1, v2) / (magnitude(v1) * magnitude(v2))
}
